/* Original Author:  Dominick O'Dierno
 * Date: 2007   odiernod@gmail.com     --Originally intended for use with DOMercury:  http://www.odierno.com/domercury
 * Description: Handles folder indexing, and options saving / loading
 *
 * --CHANGELOG--
 * 2008-05-18 - Tao Klerks:
 *  - Added detailed logging option
 *  - Added "Special Folder" handling for internationalization etc, making "DOMyDocuments" plugin redundant.
 *  - Added "Include Folder" option
 *  - Added "Include Subfolders" option
 *  - Changed option saving to use new application configuration storage
 * 2008-05-21 - Tao Klerks:
 *  - Corrected configuration references
 * 
 * NOTE: After making changes to this, you will need to physically copy the 
 * FolderIndexorPlugin.dll file to the Interfaces Folder in the DOMercury bin/Debug 
 * in order to see the changes (or debug / step through the code).
 * */

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Windows.Forms;

using DOMercury.Engine;
using DOMercury.Interfaces;
using DOMercury.Items;
using DOMercury.Utilities;

namespace FolderIndexorPlugin
{
    [DOMercuryPlugIn("Description of Plugin - Not really used anywhere")]
    public class FolderIndexorPlugin : IItemProvider
    {
        //List of folders indexed
        private List<IndexedFolder> folders;
        private bool detailedLogging = false;

        //Constructor
        public FolderIndexorPlugin()
        {
            folders = new List<IndexedFolder>();
            Load();
        }

        //Generates a list of items to add to the index
        private List<IItem> PopulateItems()
        {
            if (detailedLogging)
            {
                using (StreamWriter writer = new StreamWriter("Plugins/FolderIndexorRuns.log", true))
                    writer.WriteLine("Starting populateItems at " + DateTime.Now.ToUniversalTime());
            }

            List<IItem> items = new List<IItem>();
            foreach (IndexedFolder folder in this.folders)
            {
                if (detailedLogging)
                {
                    using (StreamWriter writer = new StreamWriter("Plugins/FolderIndexorRuns.log", true))
                        writer.WriteLine("Starting populateItems detail for " + folder.FolderPath + " at " + DateTime.Now.ToUniversalTime());
                }

                string pathRoot = Path.GetPathRoot(Application.ExecutablePath);
                string path = folder.FolderPath.Replace("^D^", pathRoot);

                if (folder.IncludeFolderItem)
                {
                    string str3 = path;
                    if (str3.EndsWith(@"\"))
                    {
                        str3 = str3.Remove(str3.Length - 1, 1);
                    }
                    List<PathItem> list2 = new List<PathItem>();
                    list2.Add(new PathItem(path, Path.GetFileName(str3)));
                    ItemsList item = new ItemsList(list2);
                    items.Add(item);
                }

                int oldCount = items.Count;

                this.GetFiles(path, folder.SearchSubFolders, folder.SearchPattern, ref items, folder.IncludeSubFolderItems);

                if (detailedLogging)
                {
                    using (StreamWriter writer = new StreamWriter("Plugins/FolderIndexorRuns.log", true))
                        writer.WriteLine("Completing populateItems detail for " + folder.FolderPath + " at " + DateTime.Now.ToUniversalTime() + ", collected " + (items.Count - oldCount) + " items.");
                }

            }

            if (detailedLogging)
            {
                using (StreamWriter writer = new StreamWriter("Plugins/FolderIndexorRuns.log", true))
                    writer.WriteLine("Completing populateItems at " + DateTime.Now.ToUniversalTime() + ", collected total of " + items.Count + " items.");
            }

            return items;
        }

        public void Load()
        {
            bool everythingOK = false;
            bool settingsFound = false;

            try
            {
                //use application-stored settings.

                //although serialization of custom derived types works fine, the deserialization fails
                // because the custom derived types (defined in the plugin assembly only) cannot be 
                // retrieved by name only using the Type.GetType static method. 
                // Instead, we need to deal with the Xml document directly to retrieve the serialized 
                // objects.

                XmlDocument settingsDoc = Interface.EngineCore.GetPluginSettingsXml("FolderIndexor");
                SerializableDictionary<string, object> allSettings = null;
                if (settingsDoc != null)
                {
                    XmlNode tempSolution = settingsDoc.SelectSingleNode("//ArrayOfIndexedFolder");
                    XmlSerializer s = new XmlSerializer(typeof(IndexedFolder[]));
                    using (XmlReader r = new XmlNodeReader(tempSolution))
                    {
                        IndexedFolder[] foldersArray = (IndexedFolder[])s.Deserialize(r);
                        folders = new List<IndexedFolder>(foldersArray);
                    }

                    XmlNode tempBoolNode = settingsDoc.SelectSingleNode("//boolean");
                    if (tempBoolNode.InnerText == "true")
                        detailedLogging = true;

                    settingsFound = true;
                    everythingOK = true;
                }
                
            }
            catch (Exception ex)
            {
                //ignore, move on to other possibilities
            }

            if (!settingsFound)
            {
                //check for old-style file
                try
                {
                    FileStream stream = new FileStream("Plugins/FileIndexorData.csv", FileMode.Open, FileAccess.Read);
                    StreamReader reader = new StreamReader(stream);

                    everythingOK = true; //looks like an old file was found, let's see if it works!
                    settingsFound = true;

                    while (!reader.EndOfStream)
                    {
                        try
                        {
                            string[] strArray = reader.ReadLine().Split(new char[] { ',' });
                            bool searchSubFolders = false;
                            bool includeFolder = false;
                            if (strArray[1].ToLower().Trim() == "true")
                            {
                                searchSubFolders = true;
                            }
                            if (strArray[2].ToLower().Trim() == "true")
                            {
                                includeFolder = true;
                            }
                            this.folders.Add(new IndexedFolder(strArray[0].Trim(), searchSubFolders, includeFolder, false, strArray[3].Trim(), false, IndexedFolder.SpecialFolderType.Documents));
                            continue;
                        }
                        catch
                        {
                            //don't worry about the exact error, just log the fact that something went wrong
                            everythingOK = false;
                            continue;
                        }
                    }
                    reader.Close();
                    stream.Close();
                }
                catch
                {
                    //don't worry about the exact error, just log the fact that something went wrong
                    everythingOK = false;
                }
            }

            if (!settingsFound)
            {
                //no previous .Net managed settings, no previous file, try to load defaults from XML template file for distribution.
                try
                {
                    using (FileStream stream = new FileStream("Plugins/FolderIndexorDefaultData.xml", FileMode.Open, FileAccess.Read))
                    {
                        StreamReader reader = new StreamReader(stream);
                        IndexedFolder[] foldersArray;
                        XmlSerializer x = new XmlSerializer(typeof(IndexedFolder[]));

                        foldersArray = (IndexedFolder[])x.Deserialize(reader);
                        folders = new List<IndexedFolder>(foldersArray);

                        reader.Close();
                        reader.Dispose();

                        everythingOK = true;
                        settingsFound = true;
                    }
                }
                catch (Exception ex)
                {
                    //give up.
                }
            }

            if (!everythingOK)
            {
                System.Windows.Forms.MessageBox.Show("FolderIndexorPlugin: Not all folders were loaded successfully.  \nPlease review plugin settings.");
                //to get rid of this message, just save plugin settings.
            }
        }

        private void GetFiles(string filepath, bool recurse, string pattern, ref List<IItem> items, bool IncludeSubFolderItems)
        {
            try
            {
                SearchOption searchSubfolders;
                if (recurse)
                    searchSubfolders = SearchOption.AllDirectories;
                else
                    searchSubfolders = SearchOption.TopDirectoryOnly;

                foreach (string str in Directory.GetFiles(filepath, pattern, searchSubfolders))
                {
                    try
                    {
                        List<PathItem> list = new List<PathItem>();
                        string fileName = Path.GetFileName(str);

                        if (fileName.EndsWith(".lnk", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".url", StringComparison.OrdinalIgnoreCase))
                            list.Add(new PathItem(str, fileName.Substring(0, fileName.Length - 4)));
                        else
                            list.Add(new PathItem(str, fileName));
                        //here could add some logic to call recursively if folder shortcuts were to be followed

                        items.Add(new ItemsList(list));

                    }
                    catch
                    {
                        //ignore all filesystem errors
                    }
                }
            }
            catch
            {
            }

            if (recurse && IncludeSubFolderItems)
            {
                try
                {
                    foreach (string str2 in Directory.GetDirectories(filepath, "*", SearchOption.AllDirectories))
                    {
                        List<PathItem> list2 = new List<PathItem>();
                        list2.Add(new PathItem(str2, Path.GetFileName(str2)));
                        ItemsList item = new ItemsList(list2);
                        items.Add(item);
                    }
                }
                catch
                {
                }
            }
        }


        public void Save()
        {
            IndexedFolder[] folderArray = new IndexedFolder[folders.Count];
            folders.CopyTo(folderArray, 0);
            SerializableDictionary<string, object> pluginSettings = new SerializableDictionary<string, object>();
            pluginSettings.Add("folders", folderArray);
            pluginSettings.Add("detailedLogging", detailedLogging);
            Interface.EngineCore.SavePluginSettings("FolderIndexor", pluginSettings);

            //check for old-style file, and try to remove (to avoid confusion)
            try
            {
                if (File.Exists("Plugins/FileIndexorData.csv"))
                    File.Delete("Plugins/FileIndexorData.csv");
            }
            catch
            {
                //if any error, eg permissions error, ignore.
            }
        }

        #region IDOMercuryPlugin Members


        //Gives DOMercury a list of items to put into its index
        public List<IItem> Items
        {
            get { return PopulateItems(); }
        }

        //Description of your plugin
        public string Description
        {
            get { return "Index any folder with the option to filter specific file types"; }
        }
        //name of your plugin
        public string Name
        {
            get { return "FolderIndexorPlugin"; }
        }

        //if adding custom icons for your custom items
        public List<IconPair> CustomIcons
        {
            get { return new List<IconPair>(); }
        }

        //Tells the Plugin Details form whether to enable the Show Options Button
        public bool HasOptions
        {
            get { return true; }
        }

        //If you have an options form, call the ShowDialog method on it here.
        public void ShowOptions()
        {
            using (FolderIndexorOptions options = new FolderIndexorOptions(this.folders, this.detailedLogging))
            {
                options.ShowDialog();
                this.folders = options.Folders;
                this.detailedLogging = options.DetailedLogging;
                this.Save();
            }
        }

        #endregion

    }
}
